﻿namespace WFBot.Data
{
    using System;
    using System.Data.Common;
    using System.Diagnostics;
    using System.Diagnostics.Contracts;
    using System.Linq;
    using Microsoft.Data.Objects;

    public class DatabaseFactory : Disposable, IDatabaseFactory
    {
        private static readonly ContextBuilder<Database> Builder = CreateBuilder();
        private readonly string _connectionString;
        private readonly DbProviderFactory _providerFactory;

        private Database _database;

        public DatabaseFactory(DbProviderFactory providerFactory, string connectionString)
        {
            Contract.Requires(providerFactory != null);
            Contract.Requires(!string.IsNullOrWhiteSpace(connectionString));

            _providerFactory = providerFactory;
            _connectionString = connectionString;
        }

        #region IDatabaseFactory Members

        public virtual Database Get()
        {
            if (_database == null)
            {
                var connection = _providerFactory.CreateConnection();
                connection.ConnectionString = _connectionString;

                _database = Builder.Create(connection);
            }

            return _database;
        }

        #endregion

        [DebuggerStepThrough]
        protected override void DisposeCore()
        {
            if (_database != null)
            {
                _database.Dispose();
            }
        }

        private static ContextBuilder<Database> CreateBuilder()
        {
            var contextBuilder = new ContextBuilder<Database>();

            var configurationTypes = typeof(DatabaseFactory).Assembly
                .GetTypes()
                .Where(
                    type =>
                    type.IsPublic && type.IsClass && !type.IsAbstract && !type.IsGenericType &&
                    typeof(StructuralTypeConfiguration).IsAssignableFrom(type) &&
                    (type.GetConstructor(Type.EmptyTypes) != null));

            foreach (var configuration in configurationTypes.Select(type => (StructuralTypeConfiguration)Activator.CreateInstance(type)))
            {
                contextBuilder.Configurations.Add(configuration);
            }

            return contextBuilder;
        }
    }
}